package com.auxgroup.smarthome.controller;

import com.alibaba.fastjson.JSONObject;
import com.auxgroup.smarthome.enterprise.service.AuxAnalysisService;
import com.auxgroup.smarthome.enterpriseapi.requestbody.auxrequest.AuxDeviceAggDataBody;
import com.auxgroup.smarthome.enterpriseapi.responsebody.auxresult.AuxAggDataResult;
import com.auxgroup.smarthome.enterpriseapi.responsebody.auxresult.AuxDatapointDetailResult;
import com.auxgroup.smarthome.enterpriseapi.responsebody.auxresult.AuxDeviceErrorDetailResult;
import com.auxgroup.smarthome.enterpriseapi.responsebody.auxresult.AuxDeviceSummaryResult;
import com.auxgroup.smarthome.enterpriseapi.responsebody.auxresult.AuxDeviceTrendResult;
import com.auxgroup.smarthome.enterpriseapi.responsebody.auxresult.AuxUserIncrHistory;
import com.auxgroup.smarthome.enterpriseapi.responsebody.auxresult.AuxUserSummaryResult;
import com.auxgroup.smarthome.enterpriseapi.responsebody.auxresult.AuxUserTotal;
import com.auxgroup.smarthome.enterpriseapi.responsebody.auxresult.AuxUserTrendResult;
import com.auxgroup.smarthome.enterpriseapi.responsebody.auxresult.DisplayData;
import com.auxgroup.smarthome.web.ApiResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.hibernate.validator.constraints.NotEmpty;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.annotations.ApiIgnore;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * @Author: laiqiuhua.
 * @Date: 2017/8/18 16:41.
 */
@Api(tags = "数据分析")
@RestController
@RequestMapping("/products")
@Validated
public class DataAnalysisController {
    @Autowired
    private AuxAnalysisService auxAnalysisService;

    @ApiOperation(value = "设备概览")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "deviceModel", value = "设备型号，可以多选", paramType = "query", dataType = "string"),
    })
    @GetMapping("/device_summary")
    public ApiResponse<AuxDeviceSummaryResult> auxDeviceSummary(String deviceModel) {
        return auxAnalysisService.auxDeviceSummary(deviceModel);
    }

    @ApiOperation(value = "设备趋势")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "start", value = "时间格式：yyyy-MM-dd", paramType = "query", dataType = "string", required = true),
            @ApiImplicitParam(name = "end", value = "时间格式：yyyy-MM-dd", paramType = "query", dataType = "string", required = true),
            @ApiImplicitParam(name = "deviceModel", value = "设备型号，可以多选", paramType = "query", dataType = "string")
    })
    @GetMapping("/device_trend")
    public ApiResponse<AuxDeviceTrendResult> auxDeviceTrend(@RequestParam("start") String start, @RequestParam("end") String end, String deviceModel) {
        return auxAnalysisService.auxDeviceTrend(start, end, deviceModel);
    }

    @ApiOperation(value = "设备各功能使用情况")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "deviceModel", value = "设备型号，可以多选", paramType = "query", dataType = "string"),
    })
    @GetMapping("/datapoint_detail")
    public ApiResponse<AuxDatapointDetailResult> auxDatapointDetail(String deviceModel) {
        ApiResponse<AuxDatapointDetailResult> apiResponse = auxAnalysisService.auxDatapointDetail(deviceModel);

        if(!apiResponse.isNotErrorCode()) {
            return apiResponse;
        }

        AuxDatapointDetailResult auxDatapointDetailResult = apiResponse.getData();
        List<AuxDatapointDetailResult.DatapointData> datapointDatas = auxDatapointDetailResult.getData();
        int total = datapointDatas.size()==0 ? 0 : datapointDatas.stream().map(datapointData-> datapointData.getTotal()).reduce(Integer::sum).get();
        auxDatapointDetailResult.setTotal(total);

        for (AuxDatapointDetailResult.DatapointData data: datapointDatas) {
            List<DisplayData> displayData = data.getChildren();
            Collections.sort(displayData, Comparator.comparing(DisplayData::getTotal, Comparator.reverseOrder()));
        }

        return apiResponse;
    }

    /**
     * 平台部署功能即删除
     *
     * @return
     */
    private AuxDatapointDetailResult getFakeDataPointDetail() {
        AuxDatapointDetailResult result = new AuxDatapointDetailResult();
        List<AuxDatapointDetailResult.DatapointData> datapointDatas = new ArrayList<>();
        for (DatapointDetailEnum datapointDetailEnum : DatapointDetailEnum.values()) {
            AuxDatapointDetailResult.DatapointData datapointData = new AuxDatapointDetailResult.DatapointData();
            datapointData.setDisplay_name(datapointDetailEnum.getDisplayName());
            datapointData.setTotal(datapointDetailEnum.getTotal());
            datapointDatas.add(datapointData);
        }
        result.setData(datapointDatas);
        return result;
    }

    @ApiOperation(value = "获取设备历史最新消息-可写数据点", notes =
            "<ul>\n" +
                    "<li>普通控制：0（CMD：0680）</li>\n" +
                    "<li>普通控制采集：1（CMD：0700）</li>\n" +
                    "<li>状态采集：2（CMD：0700）</li>\n" +
                    "<li>睡眠曲线控制：3（CMD：0680）</li>\n" +
                    "<li>睡眠曲线采集：4（CMD：0700）</li>\n" +
                    "<li>高级控制：5 （峰谷电等）（CMD：0680）</li>\n" +
                    "</ul>")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "cmd", value = "cmd", paramType = "query", dataType = "string", required = true),
            @ApiImplicitParam(name = "src", value = "src", paramType = "query", dataType = "int", required = true)
    })
    @GetMapping("/{productKey}/devices/{did}/latest_data_by_cmd_src")
    public ApiResponse getLa(@PathVariable("productKey") String pk, @PathVariable("did") String did, String cmd, Integer src) {
        return auxAnalysisService.getLatestDataByCmdAndSrc(pk, did, cmd, src);
    }

    @ApiOperation(value = "设备地理位置分布情况")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "type", value = "设备类型：device/fault", paramType = "query", dataType = "string", required = true),
            @ApiImplicitParam(name = "deviceModel", value = "设备型号，可以多选", paramType = "query", dataType = "string"),
    })
    @GetMapping("/device_distribution")
    public ApiResponse<JSONObject> auxDeviceDistribution(@RequestParam("type") String type, String deviceModel) {
        ApiResponse<JSONObject> apiResponse = auxAnalysisService.auxDeviceDistribution(type, deviceModel);

        if (!apiResponse.isNotErrorCode()) {
            return apiResponse;
        }

        JSONObject jsonObject = apiResponse.getData();
        Map<String, Integer> region = jsonObject.getObject("region", Map.class);
        if(region.size() > 0) {
            int total = region.values().stream().reduce(Integer::sum).get();
            jsonObject.put("total", total);
            region = sortMapByValueDesc(region);
            jsonObject.put("region", region);
        }

        return apiResponse;
    }

    public static Map<String, Integer> sortMapByValueDesc(Map<String, Integer> oriMap) {
        if (oriMap == null || oriMap.isEmpty()) {
            return null;
        }
        Map<String, Integer> sortedMap = new LinkedHashMap<String, Integer>();
        List<Map.Entry<String, Integer>> entryList = new ArrayList<Map.Entry<String, Integer>>(
                oriMap.entrySet());
        Collections.sort(entryList, new MapValueDescComparator());

        Iterator<Map.Entry<String, Integer>> iter = entryList.iterator();
        Map.Entry<String, Integer> tmpEntry = null;
        while (iter.hasNext()) {
            tmpEntry = iter.next();
            sortedMap.put(tmpEntry.getKey(), tmpEntry.getValue());
        }
        return sortedMap;
    }

    @ApiOperation(value = "设备故障分析")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "deviceModel", value = "设备型号，可以多选", paramType = "query", dataType = "string"),
    })
    @GetMapping("/device_error_detail")
    public ApiResponse<AuxDeviceErrorDetailResult> auxDeviceErrorDetail(String deviceModel) {
        ApiResponse<AuxDeviceErrorDetailResult> apiResponse = auxAnalysisService.auxDeviceErrorDetail(deviceModel);
        if(!apiResponse.isNotErrorCode()) {
            return apiResponse;
        }
        AuxDeviceErrorDetailResult auxDeviceErrorDetailResult = apiResponse.getData();
        int total = auxDeviceErrorDetailResult.getData().size()==0?0:auxDeviceErrorDetailResult.getData().stream().map(displayData -> displayData.getTotal()).reduce(Integer::sum).get();
        auxDeviceErrorDetailResult.setTotal(total);
        return apiResponse;
    }

    @ApiOperation(value = "用户概览")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "deviceModel", value = "设备型号，可以多选", paramType = "query", dataType = "string"),
    })
    @GetMapping("/user_summary")
    public ApiResponse<AuxUserSummaryResult> auxUserSummary(String deviceModel) {
        return auxAnalysisService.auxUserSummary(deviceModel);
    }


    @ApiOperation(value = "用户趋势")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "start", value = "时间格式：yyyy-MM-dd", paramType = "query", dataType = "string", required = true),
            @ApiImplicitParam(name = "end", value = "时间格式：yyyy-MM-dd", paramType = "query", dataType = "string", required = true),
            @ApiImplicitParam(name = "deviceModel", value = "设备型号，可以多选", paramType = "query", dataType = "string"),
    })
    @GetMapping("/user_trend")
    public ApiResponse<AuxUserTrendResult> auxUserTrend(@RequestParam(value = "start") String start, @RequestParam(value = "end") String end, String deviceModel) {
        return auxAnalysisService.auxUserTrend(start, end, deviceModel);
    }

    /**
     * 用户数量分布情况
     *
     * @param deviceModel
     * @return
     */
    @ApiOperation(value = "用户数量分布情况")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "deviceModel", value = "设备型号，可以多选", paramType = "query", dataType = "string"),
    })
    @GetMapping("/user_distribution")
    public ApiResponse<JSONObject> AuxUserDistribution(String deviceModel) {
        ApiResponse<JSONObject> apiResponse = auxAnalysisService.AuxUserDistribution(deviceModel);
        if (apiResponse.getCode() != 200) {
            return apiResponse;
        }
        JSONObject jsonObject = apiResponse.getData();
        Map<String, Integer> region = jsonObject.getObject("region", Map.class);
        if(region.size() > 0) {
            int total = region.values().stream().reduce(Integer::sum).get();
            jsonObject.put("total", total);
            region = sortMapByValueDesc(region);
            jsonObject.put("region", region);
        }
        return ApiResponse.ok(jsonObject);
    }

    @ApiOperation(value = "获取用户增长历史记录（最多查询31天以内的）")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "startDate", value = "开始时间：yyyy-MM-dd", paramType = "query", dataType = "string"),
            @ApiImplicitParam(name = "endDate", value = "结束时间：yyyy-MM-dd", paramType = "query", dataType = "string"),
    })
    @GetMapping("/user_incr_history")
    public ApiResponse<List<AuxUserIncrHistory>> auxUserIncreaseHistory(@NotEmpty(message = "开始日期不能为空") String startDate, @NotEmpty(message = "结束日期不能为空") String endDate) {
        ApiResponse<List<AuxUserIncrHistory>> apiResponse = auxAnalysisService.auxUserIncreaseHistory(startDate, endDate);
        return apiResponse;
    }

    @ApiOperation(value = "获取用户地理位置分布及排行")
    @GetMapping("/user_location_distribution")
    public ApiResponse<JSONObject> auxUserLocationDistribution() {
        ApiResponse<JSONObject> apiResponse = auxAnalysisService.auxUserLocationDistribution();
        if (!apiResponse.isNotErrorCode()) {
            return apiResponse;
        }

        JSONObject jsonObject = apiResponse.getData();
        Map<String, Integer> region = jsonObject.getObject("region", Map.class);
        if(region.size() > 0) {
            int total = region.values().stream().reduce(Integer::sum).get();
            jsonObject.put("total", total);
            region = sortMapByValueDesc(region);
            jsonObject.put("region", region);
        }

        return apiResponse;
    }

    @ApiOperation(value = "获取用户总数")
    @GetMapping("/user_total")
    public ApiResponse<AuxUserTotal> AuxUserDistribution() {
        ApiResponse<AuxUserTotal> apiResponse = auxAnalysisService.auxUserTotal();
        return apiResponse;
    }

    @ApiIgnore
    @ApiOperation(value = "聚合数据", notes = "设备聚合数据\n" +
            "start 和 end 为字符串日期，日期格式为年月日时，For Example: \"2017072111\"。\n" +
            "attrs 只能为数字类型的数据点。\n" +
            "返回的 datetime 格式\n" +
            "HOURS \"2015072010\"\n" +
            "DAYS \"20150720\"\n" +
            "WEEKS \"201529\"\n" +
            "MONTHS \"201507\"\n" +
            "YEARS \"2017\"\n" +
            "aggregator include [sum,avg,max,min]\n" +
            "unit 包含 [HOURS,DAYS,WEEKS,MONTHS,YEARS]，unit 最小的单位为 HOURS")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "start", value = "年月日时的格式", dataType = "string", paramType = "query", required = true),
            @ApiImplicitParam(name = "end", value = "年月日时的格式", dataType = "string", paramType = "query"),
            @ApiImplicitParam(name = "attrs", value = "数据点标识 可以是多个,用英文,号隔开 ", dataType = "string", paramType = "query", required = true),
            @ApiImplicitParam(name = "aggregator", value = "sum,avg,max,min", dataType = "string", paramType = "query", required = true),
            @ApiImplicitParam(name = "unit", value = "HOURS,DAYS,WEEKS,MONTHS", dataType = "string", paramType = "query", required = true)
    })
    @GetMapping("/{productKey}/devices/{did}/agg_data")
    public ApiResponse<AuxAggDataResult> wifiDeviceAggData(@PathVariable("productKey") String pk, @PathVariable("did") String did, @ApiIgnore AuxDeviceAggDataBody body) {
        return auxAnalysisService.wifiDeviceAggData(pk, did, body);
    }

}
